/*
 * Copyright © 2018 www.noark.xyz All Rights Reserved.
 *
 * 感谢您选择Noark框架，希望我们的努力能为您提供一个简单、易用、稳定的服务器端框架 ！
 * 除非符合Noark许可协议，否则不得使用该文件，您可以下载许可协议文件：
 *
 *        http://www.noark.xyz/LICENSE
 *
 * 1.未经许可，任何公司及个人不得以任何方式或理由对本框架进行修改、使用和传播;
 * 2.禁止在本项目或任何子项目的基础上发展任何派生版本、修改版本或第三方版本;
 * 3.无论你对源代码做出任何修改和改进，版权都归Noark研发团队所有，我们保留所有权利;
 * 4.凡侵犯Noark版权等知识产权的，必依法追究其法律责任，特此郑重法律声明！
 */
package xyz.noark.game.template.json;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import xyz.noark.core.annotation.tpl.TplAttr;
import xyz.noark.core.annotation.tpl.TplAttrSuffix;
import xyz.noark.core.annotation.tpl.TplFile;
import xyz.noark.core.converter.ConvertManager;
import xyz.noark.core.converter.Converter;
import xyz.noark.core.exception.ConvertException;
import xyz.noark.core.exception.TplAttrRequiredException;
import xyz.noark.core.exception.TplConfigurationException;
import xyz.noark.core.exception.UnrealizedException;
import xyz.noark.core.lang.ResourceLoader;
import xyz.noark.core.util.ClassUtils;
import xyz.noark.core.util.FieldUtils;
import xyz.noark.core.util.MethodUtils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static xyz.noark.log.LogHelper.logger;

/**
 * NoarkJson，内部中转的Json辅助类.
 *
 * @author 小流氓[176543888@qq.com]
 * @since 3.4
 */
class NoarkJson extends ResourceLoader {
    private final ConvertManager convertManager = ConvertManager.getInstance();

    /**
     * 一个文件一个对象，常用于应用的配置文件
     *
     * @param templatePath 配置文件路径
     * @param zone         版本分区，默认为""
     * @param klass        配置对象类
     * @param <T>          配置对象的泛型
     * @return 配置对象
     */
    <T> T load(String templatePath, String zone, Class<T> klass) {
        TplFile file = klass.getAnnotation(TplFile.class);
        if (file == null) {
            throw new TplConfigurationException("这不是JSON格式的配置文件类:" + klass.getName());
        }

        // 如果配表类存在Set方法，给一个警告提示...
        if (MethodUtils.existSetMethod(klass)) {
            logger.warn("配表类正常为只读模式，不应该存在Set方法噢，class={}", klass.getName());
        }

        try (InputStream is = this.newInputStream(templatePath, zone, 0, file.value())) {
            JSONObject jsonObject = JSON.parseObject(is);
            return analysisObject(klass, file.value(), jsonObject);
        } catch (IOException e) {
            throw new TplConfigurationException("JSON格式的配置文件类:" + klass.getName(), e);
        }
    }

    /**
     * 一个文件多个对象，常用于应用的策划配置文件
     *
     * @param templatePath 配置文件路径
     * @param zone         配置文件分区，默认为""
     * @param klass        配置对象类
     * @param <T>          配置对象的泛型
     * @return 配置对象集合
     */
    <T> List<T> loadAll(String templatePath, String zone, Class<T> klass) {
        TplFile file = klass.getAnnotation(TplFile.class);
        if (file == null) {
            throw new TplConfigurationException("这不是JSON格式的配置文件类:" + klass.getName());
        }

        // 如果配表类存在Set方法，给一个警告提示...
        if (MethodUtils.existSetMethod(klass)) {
            logger.warn("配表类正常为只读模式，不应该存在Set方法噢，class={}", klass.getName());
        }

        try (InputStream is = this.newInputStream(templatePath, zone, 0, file.value())) {
            JSONArray jsonArray = JSON.parseArray(is);
            List<T> result = new ArrayList<>(jsonArray.size());
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                result.add(analysisObject(klass, file.value(), jsonObject));
            }
            return result;
        } catch (IOException e) {
            throw new TplConfigurationException("JSON格式的配置文件类:" + klass.getName(), e);
        }
    }

    private <T> T analysisObject(Class<T> klass, String tplFileName, JSONObject jsonObject) {
        T result = ClassUtils.newInstance(klass);
        /* 使用工具获取，父类的属性也要判定 */
        for (Field field : FieldUtils.getAllField(klass)) {
            TplAttr[] array = field.getAnnotationsByType(TplAttr.class);
            if (array.length == 0) {
                continue;
            }

            // 后缀配置
            TplAttrSuffix suffix = field.getAnnotation(TplAttrSuffix.class);

            // 只有一个配置且没有后缀配置
            if (array.length == 1 && suffix == null) {
                final TplAttr attr = array[0];

                // 取值
                Object o = jsonObject.get(attr.name());
                if (o == null) {
                    if (attr.required()) {
                        throw new TplAttrRequiredException(klass, field, attr);
                    }
                    continue;
                }

                // 可以直接赋值
                if (field.getType() == o.getClass() || (field.getType().isPrimitive() && isWrapperType(field.getType(), o.getClass()))) {
                    FieldUtils.writeField(result, field, o);
                }
                // 需要通过 converter 转换
                else {
                    if (o instanceof JSONObject subObject) {
                        Class<?> type = field.getType();
                        FieldUtils.writeField(result, field, analysisObject(type, type.getSimpleName(), subObject));
                    } else {
                        Converter<?> converter = this.getConverter(field);
                        try {
                            FieldUtils.writeField(result, field, converter.convert(field, o.toString()));
                        } catch (Exception e) {
                            throw new ConvertException(tplFileName + " >> " + field.getName() + " >> " + o + "-->" + converter.buildErrorMsg(), e);
                        }
                    }
                }
            }
            // 只有一个配置且有后缀，那也是个多个配置
            else if (array.length == 1) {
                // 如果后缀配置小于等于0，那就没得玩了哈...
                if (suffix.step() <= 0) {
                    throw new ConvertException(klass.getName() + " >> " + field.getName() + " >> TplAttrSuffix#step=" + suffix.step() + "-->" + "不可以小于等于0.");
                }

                TplAttr attr = array[0];
                Map<String, String> data = new LinkedHashMap<>((suffix.end() - suffix.start()) / suffix.step() + 1);
                for (int i = suffix.start(); i <= suffix.end(); i += suffix.step()) {
                    final String attrName = attr.name() + i;
                    data.put(attrName, jsonObject.getString(attrName));
                }
                Converter<?> converter = this.getConverter(field);
                try {
                    FieldUtils.writeField(result, field, converter.convert(field, data));
                } catch (Exception e) {
                    throw new ConvertException(tplFileName + " >> " + field.getName() + " >> " + data + "-->" + converter.buildErrorMsg(), e);
                }
            }
            // 多个配置时，忽略后缀的
            else {
                Map<String, String> data = new LinkedHashMap<>(array.length + 1);
                for (TplAttr attr : array) {
                    data.put(attr.name(), jsonObject.getString(attr.name()));
                }
                Converter<?> converter = this.getConverter(field);
                try {
                    FieldUtils.writeField(result, field, converter.convert(field, data));
                } catch (Exception e) {
                    throw new ConvertException(tplFileName + " >> " + field.getName() + " >> " + data + "-->" + converter.buildErrorMsg(), e);
                }
            }
        }
        return result;
    }

    // 辅助方法：判断是否为基础类型和包装类型的关系
    private boolean isWrapperType(Class<?> primitive, Class<?> wrapper) {
        if (primitive == int.class) return wrapper == Integer.class;
        if (primitive == long.class) return wrapper == Long.class;
        if (primitive == double.class) return wrapper == Double.class;
        if (primitive == float.class) return wrapper == Float.class;
        if (primitive == boolean.class) return wrapper == Boolean.class;
        if (primitive == byte.class) return wrapper == Byte.class;
        if (primitive == char.class) return wrapper == Character.class;
        if (primitive == short.class) return wrapper == Short.class;
        return false;
    }

    private Converter<?> getConverter(Field field) {
        Converter<?> result = convertManager.getConverter(field.getType());
        if (result == null) {
            throw new UnrealizedException("JSON配置解析时，发现未实现的类型. field=(" + field.getType().getName() + ")" + field.getName());
        }
        return result;
    }


}